use std::fs;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::fs::OpenOptions;
use crate::reader::binary::core::{Stream, StreamError};

pub struct FileStream {
    file: fs::File,
}

impl FileStream {
    pub fn new_read_stream(filepath: &str) -> Result<FileStream, StreamError> {
        match fs::File::open(filepath) {
            Ok(f) => Ok(FileStream { file: f }),
            Err(_) => Err(StreamError::OpenError),
        }
    }

    pub fn new_write_stream(filepath: &str, create: bool, append: bool) -> Result<FileStream, StreamError> {
        let mut option = OpenOptions::new();
        option.write(true).create(create).append(append);
        match option.open(filepath) {
            Ok(f) => Ok(FileStream { file: f }),
            Err(_) => Err(StreamError::OpenError),
        }
    }
}

impl Stream for FileStream {
    fn write(&mut self, bytes: &[u8]) -> Result<usize, StreamError> {
        match self.file.write(bytes) {
            Ok(res) => Ok(res),
            Err(_) => Err(StreamError::WriteError),
        }
    }

    fn read(&mut self, buffer: &mut [u8]) -> Result<usize, StreamError> {
        let metadata_len = match self.file.metadata() {
            Ok(am) => { am.len() as usize }
            Err(_) => { return Err(StreamError::ReadError); }
        };

        if self.tell()? + buffer.len() > metadata_len {
            return Err(StreamError::ReadError);
        }

        match self.file.read(buffer) {
            Ok(res) => Ok(res),
            Err(_) => Err(StreamError::ReadError),
        }
    }

    fn seek(&mut self, to: usize) -> Result<usize, StreamError> {
        match self.file.seek(SeekFrom::Start(to as u64)) {
            Ok(res) => Ok(res as usize),
            Err(_) => Err(StreamError::SeekError),
        }
    }

    fn tell(&mut self) -> Result<usize, StreamError> {
        match self.file.seek(SeekFrom::Current(0)) {
            Ok(res) => Ok(res as usize),
            Err(_) => Err(StreamError::TellError),
        }
    }
}
